import type { ReactNode } from 'react';
import {
  useState,
  useEffect,
  useContext,
  createContext,
  useCallback,
} from 'react';
import type { User, GoTrueClient } from '@supabase/supabase-js';
import { useRouter } from 'next/router';
import supabase from 'lib/supabase';

type AuthContextType = {
  isLoaded: boolean;
  user: User | null;
  signIn: (
    email: string,
    password: string
  ) => ReturnType<GoTrueClient['signInWithPassword']>;
  signUp: (
    email: string,
    password: string
  ) => ReturnType<GoTrueClient['signUp']>;
  signOut: () => ReturnType<GoTrueClient['signOut']>;
};

export const AuthContext = createContext<AuthContextType | undefined>(
  undefined
);

export const signIn = (email: string, password: string) =>
  supabase.auth.signInWithPassword({
    email,
    password,
  });

export const signUp = (email: string, password: string) =>
  supabase.auth.signUp({
    email,
    password,
    options: { emailRedirectTo: `${process.env.BASE_URL}/app` },
  });

export const signOut = () => supabase.auth.signOut();

// Provider hook that creates auth object and handles state
function useProvideAuth(): AuthContextType {
  const router = useRouter();
  const [isLoaded, setIsLoaded] = useState(false);
  const [user, setUser] = useState<User | null>(null);

  // Initialize the user based on the stored session
  const initUser = useCallback(async () => {
    const {
      data: { session },
    } = await supabase.auth.getSession();
    if (session) {
      setUser(session.user);
    }
    setIsLoaded(true);
  }, []);

  useEffect(() => {
    initUser();
  }, [initUser]);

  useEffect(() => {
    const {
      data: { subscription },
    } = supabase.auth.onAuthStateChange(async (event, session) => {
      // Update user
      setUser(session?.user ?? null);
      setIsLoaded(true);

      // Redirect to /app if the user has signed in
      if (event === 'SIGNED_IN' && router.pathname === '/login') {
        router.push('/app');
      } else if (event === 'SIGNED_OUT') {
        router.push('/login');
      }
    });
    return () => subscription.unsubscribe();
  }, [router]);

  // Return the user object and auth methods
  return {
    isLoaded,
    user,
    signIn,
    signUp,
    signOut,
  };
}

// Provider component that wraps your app and makes auth object
// available to any child component that calls useAuth().
export function ProvideAuth({ children }: { children: ReactNode }) {
  const auth = useProvideAuth();
  return <AuthContext.Provider value={auth}>{children}</AuthContext.Provider>;
}

// Hook for child components to get the auth object and re-render when it changes.
export const useAuth = () => {
  const context = useContext(AuthContext);
  if (context === undefined) {
    throw new Error('useAuth must be used within a provider');
  }
  return context;
};
